package com.andon.microcellsimulator.common.socket;

import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;

import com.andon.microcellsimulator.common.util.EncryptUtil;
import com.andon.microcellsimulator.common.util.JsonUtil;
import com.andon.microcellsimulator.common.util.LogUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by admin on 17/12/4.
 */
class TcpServerThread extends Thread {

    private static final int PORT = 8086;
    private static final int BUFF_LEN = 1024;

    private static boolean SHUT_DOWN_FLAG = false;

    private static TcpServerThread mTcpServerThread;

    private static final Object lock = new Object();

    private Handler mHandler;

    private List<String> mWhiteList;

    private static Map<String, TcpConnectThread> socketMaps;

    private TcpServerThread() {
        socketMaps = new HashMap<String, TcpConnectThread>();
    }

    public static TcpServerThread getInstance() {

        if (mTcpServerThread == null) {
            synchronized (lock) {
                if (mTcpServerThread == null) {
                    mTcpServerThread = new TcpServerThread();
                }
            }
        }

        return mTcpServerThread;
    }

    public void setHandler(Handler handler) {
        mHandler = handler;
    }

    public void setWhiteList(List<String> whiteList) {
        mWhiteList = whiteList;
    }

    public boolean isConnectDevice(String deviceID) {
        return socketMaps.containsKey(deviceID);
    }

    public void sendCommand(String deviceID, String command, String sid) {
        if (isConnectDevice(deviceID)) {
            TcpConnectThread tcpConnect = socketMaps.get(deviceID);
            MessageModel model = new MessageModel();
            model.setDeviceID(deviceID);
            model.setMsg(command);
            model.setSid(sid);
            tcpConnect.sendMassage(JsonUtil.toJson(model).getBytes());
        } else {
            LogUtil.w("CP设备未连接,发送指令失败");
        }
    }

    public void stopConn(String did) {
        socketMaps.get(did).closeConnect(true);
    }

    public void cancel(boolean isInterrupted) {

        SHUT_DOWN_FLAG = true;

        for (Map.Entry<String, TcpConnectThread> entry : socketMaps.entrySet()) {
            entry.getValue().closeConnect(isInterrupted);
        }

        if (isInterrupted) {
            if (!isInterrupted()) {
                interrupt();
            }
        }
    }

    @Override
    public void run() {

        ServerSocket server = null;

        try {
            server = new ServerSocket(PORT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (server != null) {

            InputStream in = null;
            OutputStream out = null;
            byte[] recvBuf = null;
            byte[] sendBuf = null;

            while (true) {

                if (SHUT_DOWN_FLAG) {
                    break;
                }

                try {

                    Socket socket = server.accept();

                    if (socket != null) {

                        socket.setSoTimeout(30000);

                        recvBuf = new byte[1024];

                        in = socket.getInputStream();
                        out = socket.getOutputStream();

                        int recvlen = in.read(recvBuf);

                        if (recvlen > 0) {

                            String deviceID = getDeviceID(new String(recvBuf, 0, recvlen));

                            if (!TextUtils.isEmpty(deviceID)
                                    && mWhiteList.contains(deviceID)) {

                                out.write("1".getBytes());
                                out.flush();

                                TcpConnectThread tcpConnectThread = new TcpConnectThread(socket, deviceID);

                                socketMaps.put(deviceID, tcpConnectThread);

                                tcpConnectThread.start();

                                LogUtil.i("cp设备[ID:" + deviceID + ", port:" + socket.getPort() + "]建立tcp连接");

                                Message message = mHandler.obtainMessage();
                                message.what = 0;
                                message.obj = deviceID;
                                mHandler.sendMessage(message);

                            } else {

                                LogUtil.i("cp设备[ID:" + deviceID + ", port:" + socket.getPort() + "]未在白名单,tcp连接断开");

                                out.write("0".getBytes());
                                out.flush();

                                in.close();
                                out.close();
                                socket.close();

                            }

                        } else {
                            LogUtil.i("关闭socket连接");
                            in.close();
                            out.close();
                            socket.close();
                        }

                        in = null;
                        out = null;
                        socket = null;

                    }

                } catch (IOException e) {
                    LogUtil.e("tcp server出现异常", e);
                } finally {

                }
            }

            try {
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

    private String getDeviceID(String data) {

        if (data.length() > 1 && TextUtils.equals("1", data.substring(0, 1))) {
            return data.substring(1);
        }

        return null;

    }

    class TcpConnectThread extends Thread {

        private boolean disconnectFlag;

        private Socket socket;
        private String key;
        private OutputStream out;
        private InputStream in;

        public TcpConnectThread(Socket socket, String key) {
            this.socket = socket;
            this.key = key;
            disconnectFlag = false;
        }

        public void closeConnect(boolean isInterrupted) {

            disconnectFlag = true;

            try {
                if (out != null) {
                    out.close();
                    out = null;
                }
                if (in != null) {
                    in.close();
                    in = null;
                }
                if (socket != null && socket.isConnected()) {
                    socket.close();
                    socket = null;
                }
            } catch (IOException e) {
                LogUtil.w("关闭与[" + key + "]的连接时发生错误:" + e);
            }

            if (isInterrupted) {
                if (!isInterrupted()) {
                    interrupt();
                }
            }
        }

        /**
         * 发送数据
         * @param bytes
         * @throws IOException
         */
        public void sendMassage(byte[] bytes) {
            try {
                if (out == null)
                    out = socket.getOutputStream();
                out.write(bytes);
                out.flush();
                LogUtil.i("已向CP设备[" + key + "]发送数据:" + Arrays.toString(bytes));
            } catch (Exception e) {
                LogUtil.e("发送消息失败");
                closeConnect(false);
                socketMaps.remove(key);

                Message message = mHandler.obtainMessage();
                message.what = 1;
                message.obj = key;
                mHandler.sendMessage(message);
            }
        }

        @Override
        public void run() {
            byte[] receivBuf = new byte[BUFF_LEN];
            int recvMsgSize;

            try {
                in = socket.getInputStream();
                out = socket.getOutputStream();

                while (true) {

                    if (disconnectFlag) {
                        break;
                    }

                    Arrays.fill(receivBuf, (byte) 0x00);

                    recvMsgSize = in.read(receivBuf);

                    if (recvMsgSize == -1) {
                        // 客户端关闭socket输出流
                        break;
                    }

                    if (recvMsgSize > 0) {

                        String receivedData = new String(receivBuf, 0, recvMsgSize);

                        LogUtil.i("Receive form [port=" + socket.getPort() + "]:" + receivedData);



                        // 判断是否为心跳包
                        if (!TextUtils.equals(receivedData, key)) {
                            MessageModel messageModel = JsonUtil.toObj(receivedData, MessageModel.class);

                            // 如果为CP上报指令，则编辑为json模型
                            if (messageModel == null) {
                                messageModel = new MessageModel();
                                messageModel.setDeviceID(key);
                                messageModel.setMsg(receivedData);
                                messageModel.setSid(EncryptUtil.getRandomString());
                            }

                            Message message = mHandler.obtainMessage();
                            message.what = 2;
                            message.obj = messageModel;
                            mHandler.sendMessage(message);
                        }
                    }

                }

            } catch (Exception e) {
                LogUtil.e("与设备 [ID:" + key + ", port:" + socket.getPort() + "]的连接出现异常:" + e);
            } finally {
                closeConnect(false);
                socketMaps.remove(key);
                Message message = mHandler.obtainMessage();
                message.what = 1;
                message.obj = key;
                mHandler.sendMessage(message);
            }
        }

        public int getport() {
            return socket.getPort();
        }

    }
}
